<?php
// @formatter:off
/**
 * @file WCGroupsHolder.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-08-20 00:23:06 +0000 (Tue, 20 Aug 2013) $
 *
 * $Id: WCGroupsHolder.php 102 2013-08-20 00:23:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0.0.2/includes/holders/WCGroupsHolder.php $
 */
// @formatter:on

require_once "{$wcPaths["includes-directory"]}/representations/WCGroup.php";

/**
 * @class WCGroupsHolder This class is a holder for representations of table
 * '%groups', in other words, a holder for WCGroup objects.
 */
 class WCGroupsHolder {
	/**
	 * @var WCGroupsHolder Pointer to the singleton instance.
	 */
	protected static $_Instance;
	/**
	 * @var WCGroup[] This is the list of configured groups.
	 */
	protected $_groups = false;
	/**
	 * @var int[string] This list associate group names with their IDs.
	 */
	protected $_groupsInverted = false;
	/**
	 * @var WCConnection Database pointer shortcut.
	 */
	protected $_db;
	/**
	 * @var string Database tables' prefix shortcut.
	 */
	protected $_dbprefix;
	/**
	 * @var WCContainer List of public groups.
	 */
	protected $_public = false;
	/**
	 * Class constructor.
	 */
	protected function __construct() {
		//
		// Loading global pointers.
		global $wcDatabaseAccess;
		//
		// Loading shortcuts.
		// @{
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $wcDatabaseAccess["prefix"];
		// @}
		//
		// Calling main loader.
		$this->load();
	}
	/**
	 * Class destructor.
	 */
	public function __destruct() {
		//
		// Freeing memory.
		unset($this->_groups);
		unset($this->_groupsInverted);
	}
	/**
	 * Prevent users to clone the instance.
	 */
	public function __clone() {
		trigger_error(__CLASS__.": Clone is not allowed.", E_USER_ERROR);
	}
	//
	// Public methods.
	/**
	 * Creates a new group with default configurations
	 *
	 * @param string $name Name for the group.
	 * @param string $directory Root directory.
	 * @param string $trash Sub-directory where garbage has to be dropped.
	 * @return int Returns the id for the new group. On failure, returns false.
	 */
	public function add($name, $directory, $trash) {
		//
		// Loading global pointers.
		global $wcDefaults;
		global $wcDirectoriesHolder;
		//
		// Setting default value to be returned.
		$id = false;
		//
		// Checking that Read Only Mode is not activated.
		if(!$wcDefaults["read-only"]) {
			//
			// Building group's root directory full path.
			$directory = WCSanitizer::SystemPath($directory);
			//
			// Building group's trash directory full path.
			$trashPath = WCSanitizer::SystemPath("{$directory}{$trash}");
			//
			// Checking that:
			//	- The root directory is a valid directory.
			//	- If there's a trash directory specified, it is a
			//	  valid directory.
			if(is_dir($directory) && (!$trash || is_dir($trashPath))) {
				//
				// Creating a query to insert a new item.
				// @{
				$stmt = false;
				$stmtID = __CLASS__."::".__FUNCTION__;
				if($this->_db->has($stmtID)) {
					$stmt = $this->_db->get($stmtID);
				} else {
					$query = "insert\n";
					$query .= "        into {$this->_dbprefix}groups (\n";
					$query .= "                grp_name, grp_root, grp_trash)\n";
					$query .= "        values (:name, :directory, :trash)\n";
					$stmt = $this->_db->prepare($stmtID, $query);
				}
				// @}
				//
				// Building database parameters.
				$params = array(
					":name" => $name,
					":directory" => $directory,
					":trash" => $trash
				);
				//
				// Attempting to create the new group.
				if($stmt->execute($params)) {
					//
					// At this point, group was created.
					$id = $this->_db->lastInsertId();
				}
			}
			//
			// Reloading all groups.
			$this->load();
			//
			// When a new group is created it has no directories,
			// therefore, it is force to have it's first one.
			if($id) {
				//
				// Always, first directory is slash... no, not the
				// guitarist.
				$dirname = "/";
				//
				// Getting the new group.
				$group = $this->groupById($id);
				//
				// Adding the first directory.
				$wcDirectoriesHolder->create($dirname, $group);
			}
			//
			// Adding trash directory.
			if($id && $trash) {
				//
				// Getting the new group.
				$group = $this->groupById($id);
				//
				// Adding trash directory.
				$wcDirectoriesHolder->create($trash, $group);
			}
		}
		//
		// Returning newly created object.
		return $id;
	}
	/**
	 * This method allows to obtain a group object based on its id.
	 *
	 * @param int $id Group's identifier.
	 * @return WCGroup Returns a group's pointer. On error it returns false.
	 */
	public function groupById($id) {
		//
		// Checking if it's a valid group id.
		if(isset($this->_groups[$id])) {
			//
			// Returning group's pointer.
			return $this->_groups[$id];
		} else {
			//
			// Requested id belongs to no group, false is returned.
			return false;
		}
	}
	/**
	 * This method allows to obtain a group object based on its name.
	 *
	 * @param string $name Group's name.
	 * @return WCGroup Returns a group's pointer. On error it returns false.
	 */
	public function groupByName($name) {
		//
		// Checking if it's a valid group id.
		if(isset($this->_groupsInverted[$name])) {
			//
			// Returning group's pointer based on its ID.
			return $this->groupById($this->_groupsInverted[$name]);
		} else {
			//
			// Requested name belongs to no group, false is returned.
			return false;
		}
	}
	/**
	 * This method allows to get a list of available group IDs.
	 *
	 * @return int[] Returns a list of IDs.
	 */
	public function groupIds() {
		return array_values($this->_groupsInverted);
	}
	/**
	 * This methods allows to obtain all system configured groups.
	 *
	 * @return WCGroup[] List of loaded groups.
	 */
	public function & groups() {
		return $this->_groups;
	}
	/**
	 * This method allows to get a list of public groups..
	 *
	 * @return WCGroup[] Returns a list of public groups.
	 */
	public function publicGroups() {
		return $this->_public->items();
	}
	/**
	 * This method allows to load all internal lists.
	 *
	 * @return boolean Returns true when it successfully loads.
	 */
	public function reload() {
		//
		// Restarting internal lists.
		// @{
		$this->_public = false;
		$this->_groups = false;
		$this->_groupsInverted = false;
		// @}
		//
		// Calling main loader and returning its result.
		return $this->load();
	}
	//
	// Protected methods.
	/**
	 * The method creates all required containers.
	 */
	protected function createContainers() {
		//
		// Public group container.
		if($this->_public == false) {
			//
			// Creating a list for public groups.
			$this->_public = new WCContainer($this);
		}
	}
	/**
	 * This method is the one in charge of loading main internal list and
	 * trigger other lists to load.
	 *
	 * @return boolean Returns true when it successfully loads.
	 */
	protected function load() {
		//
		// Setting a default value to be returned.
		$out = true;
		//
		// Creating containers.
		$this->createContainers();
		//
		// Stating a new list for group objects.
		$this->_groups = array();
		//
		// Starting a new list for groups' name-IDs associations.
		$this->_groupsInverted = array();
		//
		// Creating a statement to load the list of groups.
		// @{
		$stmt = false;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  grp_id,\n";
			$query .= "        grp_name\n";
			$query .= "from    {$this->_dbprefix}groups\n";
			$query .= "order by grp_name asc";
			$stmt = $this->_db->prepare($stmtID, $query);
		}
		// @}
		//
		// Requesting groups from database.
		if($stmt->execute(array())) {
			//
			// Walking over each result.
			foreach($stmt->fetchAll() as $row) {
				//
				// Creating a new group object and adding it to
				// the internal list
				$this->_groups[$row["grp_id"]] = new WCGroup($this, $row["grp_id"]);
				//
				// Creating an association between current group
				// and its ID.
				$this->_groupsInverted[$row["grp_name"]] = $row["grp_id"];
				//
				// If it's a public group, it is added to a
				// container.
				if($this->_groups[$row["grp_id"]]->isPublic()) {
					$this->_public->addItem($row["grp_id"], $this->_groups[$row["grp_id"]]);
				}
			}
			//
			// At this point, everything seems to be okey.
			$out = true;
		}
		//
		// Returning operation result.
		return $out;
	}
	//
	// Public class methods.
	/**
	 * This class method allows to get the sole instance of this holder.
	 *
	 * @return WCGroupsHolder Returns the singleton instance of this class.
	 */
	public static function Instance() {
		//
		// Checking if there's no instance yet.
		if(!isset(self::$_Instance)) {
			$c = __CLASS__;
			//
			// Creating the sole instance.
			self::$_Instance = new $c();
		}
		//
		// Returning the class instance.
		return self::$_Instance;
	}
}
?>